/*
 * SyncServiceImpl.java
 *
 * Created on 02. Juli 2009, 11:08
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package fhv.itb.mst2955.server;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import fhv.itb.mst2955.client.SyncService;
import fhv.itb.mst2955.client.model.AddressProxy;
import fhv.itb.mst2955.client.model.ChangeObject;
import fhv.itb.mst2955.client.model.ContactProxy;
import fhv.itb.mst2955.client.model.UpdateObject;
import fhv.itb.mst2955.server.persistence.Address;
import fhv.itb.mst2955.server.persistence.Contact;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.NotSupportedException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;

/**
 *
 * @author Malcolm
 */
public class SyncServiceImpl extends RemoteServiceServlet implements
        SyncService {

    @Resource
    private UserTransaction utx;

    @PersistenceContext(unitName = "AddressBookNBPU")
    private EntityManager em;

    private AddressServiceImpl addressService;
    ContactServiceImpl clientService;

    /**
     * Synchronizes the provided list of object with the server-side database.
     *
     * @param changedObjects
     * The list of new, deleted or updated objects from the client.
     *
     * @return
     * A list of UpdateObjects that require user input for conflict resolution.
     */
    public Vector<ChangeObject> update(Vector<ChangeObject> changedObjects) {
        Vector<ChangeObject> result = new Vector<ChangeObject>();
        clientService = new ContactServiceImpl();
        clientService.setUserTransaction(utx);
        clientService.setEntityManager(em);
        addressService = new AddressServiceImpl();
        addressService.setUserTransaction(utx);
        addressService.setEntityManager(em);

        for (ChangeObject co : changedObjects) {
//            if (co.getObjectType().equals(ChangeObject.TYPE_ADDRESS)) objectForResultList = addressService.handleUpdate(co);
//            else if (co.getObjectType().equals(ChangeObject.TYPE_CLIENT)) objectForResultList = clientService.handleUpdate(co);

            UpdateObject objectForResultList = handleUpdate(co);
            if (objectForResultList != null) {
                co.setUpdateObject(objectForResultList);
                result.add(co);
            }
        }

        return result;
    }


    /**
     * Handles the synchronization of the provided object with the server's database.
     *
     * @param co
     * The object to be synchronized.
     *
     * @return
     * The server-side domain object if a conflict occurs, null otherwise.
     */
    public UpdateObject handleUpdate(ChangeObject co) {
        String changeType = co.getChangeType();
        String typeName = co.getObjectType();
        UpdateObject uo = co.getUpdateObject();

        try {
            utx.begin();
            em.joinTransaction();

            if (changeType.equals(ChangeObject.DELETED)) {
                if (typeName.equals(ChangeObject.TYPE_ADDRESS)) {
                    Address a = em.find(Address.class, co.getObjectId());
                    em.remove(a);
                } else if (typeName.equals(ChangeObject.TYPE_CONTACT)) {
                    Contact c = em.find(Contact.class, co.getObjectId());
                    em.remove(c);
                }
                utx.commit();
                return null;
            } else if (uo != null) {
                if (typeName.equals(ChangeObject.TYPE_ADDRESS)) {
                    Address a = AddressServiceImpl.createAddressFromProxy((AddressProxy) uo);
                    if (changeType.equals(ChangeObject.MODIFIED)) {
                        Address sa = em.find(Address.class, a.getId());
                        if (sa.getVersionNr() < a.getVersionNr()) em.merge(a);
                        else return addressService.createAddressProxyFromAddress(sa);
                    }
                    
                    if (changeType.equals(ChangeObject.NEW)) {
                        em.merge(a);
                        em.persist(a);
                    }
                } else if (typeName.equals(ChangeObject.TYPE_CONTACT)) {
                    Contact c = ContactServiceImpl.createContactFromProxy((ContactProxy) uo);
                    if (changeType.equals(ChangeObject.MODIFIED)) {
                        Contact sc = em.find(Contact.class, c.getId());
                        if (sc.getVersionNr() < c.getVersionNr()) em.merge(c);
                        else return clientService.createContactProxyFromContact(sc);
                    }
                    em.merge(c);
                    if (changeType.equals(ChangeObject.NEW)) em.persist(c);
                }
                utx.commit();
                return null;
            }
        } catch  (NotSupportedException ex) {
            Logger.getLogger(AddressServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SystemException ex) {
            Logger.getLogger(AddressServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
        } catch (Exception e) {
            try {
                Logger.getLogger(AddressServiceImpl.class.getName()).log(Level.SEVERE, null, e);
                utx.rollback();
            } catch (IllegalStateException ex) {
                Logger.getLogger(AddressServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
            } catch (SecurityException ex) {
                Logger.getLogger(AddressServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
            } catch (SystemException ex) {
                Logger.getLogger(AddressServiceImpl.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        return null;
    }

}
